﻿#include <QDebug>
#include <QDateTime>
#include "localcapture.hpp"

#include "limathread.h"
#include "mainwindow.h"
#include <set>
#include <iostream>

//const std::string ROOM_ID = "1300";
const std::string ROOM_ID = "408881";
std::string USER_ID;
//uint32_t videoCodecPriority[2]={1,2};
uint32_t videoCodecPriority[2]={1,2};


MainDemoWindow*         limaThread::_ownerWindow=nullptr;
void *                  limaThread::_engine=nullptr;
std::mutex              limaThread::_engineMutex;

bool limaThread::_isVideoSetting = false;
bool limaThread::_isAudioSetting = false;
LimaEngineType           limaThread::_defaulttype=VSTREAM;

std::set<std::string> subscribeUsers = {
    "44444"
};


limaThread::limaThread(MainDemoWindow*  _owner){
     _ownerWindow=_owner;
     _isStop = false;
     _engine = nullptr;

     srand(time(nullptr));
     //USER_ID = std::to_string(100+rand() % 900);
     USER_ID = std::to_string(88888);
     _defaulttype=VSTREAM;
     uint ori_size=QThread::stackSize();
     qDebug()<<"ori_size:"<<ori_size;
     if(ori_size<=8*1024*1024)
            ori_size*=ori_size*4;
     QThread::setStackSize(ori_size);
}

void limaThread::closeThread(){
    _isStop = true;
    SDL_Event event;
    event.type = SDL_QUIT;
    SDL_PushEvent(&event);
}



void limaThread::OnGotAudioFrame(const char* audioData,
                                uint32_t frequency,
                                uint32_t nBytesPerSample,
                                uint32_t number_of_channels,
                                uint32_t number_of_samplesperchanel){
        std::lock_guard<std::mutex> lock(_engineMutex);
        if (_engine != nullptr) {
//			InfoLog_lima("send audio:%lu",number_of_frames);
            if(!_isAudioSetting){
                LimaAudioParameters parameters;
                parameters.bitsPerSample = nBytesPerSample*8;
                parameters.sampleRate = frequency;
                parameters.channels = number_of_channels;
                parameters.frames = number_of_samplesperchanel;
    //			InfoLog_lima("sampleRate:%d,bitsPerSample:%d,channels:%d,frames:%d,number_of_samples:%d", parameters.sampleRate, parameters.bitsPerSample, parameters.channels, parameters.frames,number_of_samplesperchanel);
                LimaSetAudioParameters(_engine, &parameters);
                _isAudioSetting=true;
            }

            LimaSendAudio(_engine, audioData, number_of_samplesperchanel*number_of_channels*nBytesPerSample, SDL_GetTicks()*1000);
        }
}


void limaThread::OnGotVideoFrame(uint32_t w,
                                uint32_t h,
                                const char* y,
                                uint32_t y_size,
                                const char* u,
                                uint32_t u_size,
                                const char* v,
                                uint32_t v_size,
                                uint64_t timestamp){
        std::lock_guard<std::mutex> lock(_engineMutex);
        if (_engine != nullptr) {
//			InfoLog_lima("%s","send video");
            if(!_isVideoSetting){
                LimaVideoParameters parameters;
                parameters.width = w;
                parameters.height = h;
                LimaSetVideoParameters(_engine, &parameters);
                _isVideoSetting=true;
            }
            LimaSendVideo(_engine,w,h, y, y_size, u, u_size, v, v_size, timestamp);
//            qDebug()<<"timestamp:"<<timestamp;

        }
         _ownerWindow->OnGotMyVideoFrame(w, h,y,y_size,u,u_size,v,v_size,timestamp);
}
std::string limaThread::GetRoomId(){
    return ROOM_ID;
}
std::string limaThread::GetUserId(){
    return USER_ID;
}
void limaThread::Slot_SetVideoParam(){
    LimaVideoParameters parameters;
    parameters.width = 1920;
    parameters.height = 1080;
    LimaSetVideoParameters(_engine, &parameters);
}
LimaEngineType limaThread::checkTypeFromDescStr(const QString  typedesc){
    LimaEngineType curType=LIMA;
    if(typedesc.contains("LIMA")){
        curType=LIMA;
    }else if(typedesc.contains("VSTREAM")){
        curType=VSTREAM;
    }
    _ownerWindow->OnEngineTypeChanged();
    return curType;
}
void limaThread::Slot_switchEnginType(const QString & typedesc){
    LimaEngineType curType=checkTypeFromDescStr(typedesc);
    if(_defaulttype!=curType){
        _defaulttype=curType;
        //类型切换
        qDebug()<<"处理类型切换";
        SDL_Event event;
        event.type = SDL_USEREVENT;
        event.user.code = USEREVENT_USER_SETENGINETYPE;
        SDL_PushEvent(&event);
    }
}
void limaThread::SendMsg(const QString & msg,const QString & peerId){
    SDL_Event event;
    event.type = SDL_USEREVENT;

    event.user.code = USEREVENT_USER_SENDMESSAGE;
    event.user.data1 = new QString(msg);
    event.user.data2 = new QString(peerId);


    SDL_PushEvent(&event);

}
static void SaveAvFrame(const QString &path, const char *y, size_t y_size,
                        const char *u, size_t u_size,
                        const char *v, size_t v_size,
                        uint64_t timestamp,uint32_t w,uint32_t h) {
    FILE *fDump = fopen(path.toLatin1().data(), "ab");
    assert(fDump != nullptr);
    uint32_t pitchY = w;
    uint32_t pitchU = w / 2;
    uint32_t pitchV = w / 2;
    uint8_t *avY = (uint8_t *)y;
    uint8_t *avU = (uint8_t *)u;
    uint8_t *avV = (uint8_t *)v;
    for (int i = 0; i < h; i++) {
        fwrite(avY, w, 1, fDump);
        avY += pitchY;
    }
    for (int i = 0; i < h/2; i++) {
        fwrite(avU, w/2, 1, fDump);
        avU += pitchU;
    }
    for (int i = 0; i < h/2; i++) {
        fwrite(avV, w/2, 1, fDump);
        avV += pitchV;
    }
    fclose(fDump);
    //qDebug() << "save on video frame,  width:" << w << " height:" << h;
}

void limaThread::InitFakeAudioCapturer() {
    auto thread =  new std::thread([]() {
        //baza::SetThreadName("fakeaudiocapturer");
        while (!true) {
                LimaAudioParameters parameters;
                parameters.bitsPerSample = 8;
                parameters.channels = 2;
                parameters.frames = 480;
                parameters.sampleRate = 48000;
                {
                    std::lock_guard<std::mutex> lock(_engineMutex);
                    if (_engine != nullptr) {
                        LimaSetAudioParameters(_engine, &parameters);
                        std::string pcm;
                        pcm.resize(parameters.bitsPerSample / 8 * parameters.channels * parameters.frames);
                        LimaSendAudio(_engine, (const char *)pcm.data(), pcm.size(), time(NULL)*1000);
                    }
                }
                usleep(1000*1000 / (parameters.sampleRate / parameters.frames));
            }
        });
        thread->detach();
        delete thread;
}
void limaThread::Slot_connPeer(){
    assert(_engine != nullptr);
    qDebug()<<"subscribe user:"<<_ownerWindow->getPeerid().toLatin1().data();
    LimaSubscribeUserParameters subParas={{1,2}};
//    memcpy(subParas.videoCodecPriority,videoCodecPriority,sizeof(videoCodecPriority));
    LimaSubscribeUser(_engine, _ownerWindow->getPeerid().toLatin1().data(),ALL,&subParas);
}
void limaThread::SetUpEngine(){
    if(_engine){
        LimaDestroyEngine(_engine);
        _engine=nullptr;
        LimaDestroy();
    }
    //LimaSetEnvValue("customvideodecoder","true");
    //LimaSetEnvValue("logger_only_limalog","true");
    LimaInit();
     //LimaInit("ws://192.168.60.119","{\"name:\":\"Coolio\"}");
     //LimaInit("ws://192.168.60.168:81");
     //LimaInit("ws://192.168.60.113");
     assert(_engine == nullptr);
     assert(_defaulttype==LIMA||_defaulttype==VSTREAM);
     struct LimaEngineParameters params;
     //params.isAutoPlayAudio =true;
     //params.isAutoRecordAudio= true;
     params.isH265Supported=false;
     if(_defaulttype==VSTREAM)
        // _engine = LimaCreateEngine("wss://10.0.11.3","{\"name:\":\"Coolio\"}",_defaulttype,this,&params);
         _engine = LimaCreateEngine("wss://gw.vzan.com","{\"name:\":\"Coolio\"}",_defaulttype,this,&params);
     else
        // _engine = LimaCreateEngine("ws://lianmai.weizan.cn","{\"name:\":\"Coolio\"}",_defaulttype,this,&params);
        _engine = LimaCreateEngine("ws://192.168.0.106:80","{\"name:\":\"Coolio\"}",_defaulttype,this,&params);

     InitAudioCapturer(OnGotAudioFrame);
     //InitFakeAudioCapturer();
     InitVideoCapturer(OnGotVideoFrame);
     std::cout << "main end..." << std::endl;
     LimaSetOnJoinChannel(_engine, [](PLimaEngine _engine, int reason) {
          qDebug() << "join channel, reason:" << reason;
     });
     LimaSetOnUserJoined(_engine, [](PLimaEngine _engine, const char *uid) {
         qDebug() << "=======on user join, uid:" << uid << " subscribe it"<<",threadId"<<QThread::currentThreadId ()  ;
 //        (static_cast<limaThread*>(LimaGetUserData(_engine)))->_ownerWindow->On_NewUser(uid);
         (static_cast<limaThread*>(LimaGetUserContext(_engine)))->emit Sig_NewUser(uid);
         LimaSubscribeUserParameters subParas={{1,2}};
         memcpy(subParas.videoCodecPriority,videoCodecPriority,sizeof(subParas.videoCodecPriority));
         LimaSubscribeUser(_engine, uid,ALL,&subParas);
     });
     LimaSetOnUserClosed(_engine, [](PLimaEngine engine, const char *uid, int reason) {
         qDebug() << "on user closed, uid:" << uid;
         LimaUnSubscribeUser(_engine, uid,ALL);
         (static_cast<limaThread*>(LimaGetUserContext(_engine)))->emit Sig_UserLeaved(uid);

     });
     LimaSetOnVideoYUVFrame(_engine, [](PLimaEngine engine, const char * uid, const char *y, uint32_t ySize, const char *u, uint32_t uSize, const char *v, uint32_t vSize, LimaYUVFormat format, uint32_t width, uint32_t height, int64_t timestamp) {
//           qDebug() << "on video frame, ySize:" << ySize << " uSize:" << uSize << " vSize:" << vSize<<" width:"<<width<<" height:"<<height ;

          if(static_cast<limaThread*>(LimaGetUserContext(engine))->_ownerWindow)
             (static_cast<limaThread*>(LimaGetUserContext(engine)))->_ownerWindow->OnGotUserVideoFrame(uid,width,height,y,ySize,u,uSize,v,vSize,timestamp);

        // static int i=0;
        // QString filename=QString("yuv_remote_%1_%2").arg(uid).arg(i++);
         //SaveAvFrame(filename,y,ySize,u,uSize,v,vSize,timestamp,width,height);
        // qDebug() << "on video frame, uid:" << uid << " width:" << width << " height:" << height;
     });
     LimaSetOnAudioFrame(_engine, [](PLimaEngine engine, const char *uid, const char *pcm, uint32_t bitsPerSample, uint32_t sampleRate, uint32_t numberOfChannels, int64_t numberofFrames, int64_t timestamp) {
         //qDebug() << "on audio frame, bitsPerSample:" << bitsPerSample << " sampleRate:" << sampleRate << " numberOfChannels:" << numberOfChannels ;

         if (sampleRate != 48000) {
             return;
         }

         if(static_cast<limaThread*>(LimaGetUserContext(engine))->_ownerWindow)
             (static_cast<limaThread*>(LimaGetUserContext(engine)))->_ownerWindow->OnGotUserAudioFrame(uid,pcm,bitsPerSample,sampleRate,numberOfChannels,numberofFrames,timestamp);

        //SDL_QueueAudio(_audiodevice, pcm, numberOfChannels*numberofFrames*bitsPerSample/8);
     });
     LimaSetOnMessage(_engine, [](PLimaEngine engine, const char *fromUid, const char *toUid, const char *message, uint32_t size) {
         qDebug() << "on message, message:" << message << " size:" << size ;
         if(static_cast<limaThread*>(LimaGetUserContext(engine))->_ownerWindow)
          _ownerWindow->emit Sig_ShowMessage(QString("on receive message, message:%1").arg(message));
     });
     LimaSetOnSendMsg(_engine, [](PLimaEngine engine, uint32_t messageId, int reason) {
         //qDebug() << "on send msg, messageId:" << messageId;
         if(static_cast<limaThread*>(LimaGetUserContext(engine))->_ownerWindow)
         _ownerWindow->emit Sig_ShowMessage(QString("on send msg, messageId:%1").arg(messageId));
     });



     LimaSetOnStats(_engine, [](PLimaEngine engine, const char *stats, uint32_t size) {
         if(size <2 ){
             qDebug() << "stats:" << stats<<",size:"<<size ;
         }

     });

     if(_defaulttype==VSTREAM){
         // 如果是VSTREAM类型，那么先定义用户的状态
         for (const auto &subscribeUser : subscribeUsers) {
             LimaSubscribeUserParameters subParas;
             memcpy(subParas.videoCodecPriority,videoCodecPriority,sizeof(subParas.videoCodecPriority));
             LimaSubscribeUser(_engine, subscribeUser.data(), LimaSubscribeType::ALL,&subParas);
         }
     }


     LimaJoinChannel(_engine, ROOM_ID.data(), USER_ID.data());
}
void limaThread::run(){
    msleep(100);
    qDebug()<<"current thread id"<<QThread::currentThread();
    _defaulttype=checkTypeFromDescStr(_ownerWindow->getEnginTypeDescStr());
    SetUpEngine();
    /* Show the list of available drivers */
    SDL_Log("Available audio drivers:");
    for (int i = 0; i < SDL_GetNumAudioDrivers(); ++i) {
        SDL_Log("%i: %s", i, SDL_GetAudioDriver(i));
    }

    SDL_Log("Using audio driver: %s\n", SDL_GetCurrentAudioDriver());



    SDL_FlushEvents(SDL_AUDIODEVICEADDED, SDL_AUDIODEVICEREMOVED);
    SDL_EventState(SDL_WINDOWEVENT, SDL_IGNORE);


    static int done = 0;

    while (!done) {
        SDL_Event event;
        while (SDL_PollEvent(&event) > 0) {
            switch (event.type) {
                case SDL_USEREVENT:{
                        if(event.user.code == USEREVENT_USER_SENDMESSAGE){
                            std::lock_guard<std::mutex> lock(_engineMutex);
                            const QString & msg=*(( QString *)event.user.data1);
                            const QString & peerId=*(( QString *)event.user.data2);

                            LimaSendMsg(_engine,msg.toLatin1().data(),msg.toLatin1().size(),peerId.toLatin1().data());

                            delete event.user.data1;
                            delete event.user.data2;

                        }else if(event.user.code==USEREVENT_USER_SETENGINETYPE){
                            std::lock_guard<std::mutex> lock(_engineMutex);
                            SetUpEngine();
                        }
                        break;
                }
                case SDL_QUIT:{
                        qDebug()<<"quit lima service";
                        std::lock_guard<std::mutex> lock(_engineMutex);
                        if(_engine){
                            LimaDestroyEngine(_engine);
                            _engine=nullptr;
                        }
                        LimaDestroy();
                        done = 1;
                        break;
                }
            }
        }
        SDL_Delay(100);
    }
}
